{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "557e620e-cb73-427a-90ee-f5d0794dfc16",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: domino.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "834a079d-fb9f-43e1-95b3-1e373a7096fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"domino\")\n",
    "\n",
    "# create a box tetrahedral mesh scaled to domino proportions\n",
    "V, F, T = app.mesh.box().tetrahedralize().scale(0.1, 0.25, 0.025)\n",
    "app.asset.add.tet(\"block\", V, F, T)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# arrange dominoes in a spiral pattern\n",
    "R, minR, d, N, C = 1.0, 0.5, 0.15, 4096, 2\n",
    "blocks, xz = [], None\n",
    "for i in reversed(range(N)):\n",
    "    t = 2.0 * C * np.pi * i / N - np.pi / 2\n",
    "    r = (R - minR) * i / N + minR\n",
    "    angle = 180 * t / np.pi\n",
    "    x, z = -r * np.cos(t), r * np.sin(t)\n",
    "    # only add dominoes with sufficient spacing\n",
    "    if xz is None or np.linalg.norm(np.array([x, z]) - xz) > d:\n",
    "        blocks.append(scene.add(\"block\").at(x, 0, z).rotate(angle, \"y\"))\n",
    "        xz = np.array([x, z])\n",
    "\n",
    "# add first domino tilted to start the chain reaction\n",
    "blocks.append(scene.add(\"block\").at(-0.1, 0.25, -R).rotate(90, \"y\").rotate(-20, \"z\"))\n",
    "\n",
    "# set material properties for all dominoes\n",
    "gap = 1e-3\n",
    "for obj in blocks:\n",
    "    (\n",
    "        obj.param.set(\"contact-gap\", gap)\n",
    "        .set(\"friction\", 0.1)\n",
    "        .set(\"poiss-rat\", 0.49)\n",
    "    )\n",
    "\n",
    "# add invisible floor\n",
    "wall = scene.add.invisible.wall([0, scene.min(\"y\") - 0.5 * gap, 0], [0, 1, 0])\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# set preview options\n",
    "opts = {\"flat_shading\": True, \"wireframe\": True}\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e6f26d9-03d8-4e50-8315-7d41afbc8532",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session parameters\n",
    "(\n",
    "    session.param.set(\"min-newton-steps\", 32)\n",
    "    .set(\"dt\", 0.01)\n",
    "    .set(\"fps\", 30)\n",
    "    .set(\"frames\", 250)\n",
    ")\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7631339c-a669-40e0-a67c-92480efd7d24",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview(options=opts)\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f3755897-773b-4541-9d6b-61eca227a9f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an animation from the simulation results\n",
    "session.animate(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1c776ea-d010-43e9-9110-bb80e956896d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export the animation to file\n",
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7338acaa-f007-48c5-94a8-e481dd0f8cfc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
